package filerouter

import (
	"encoding/json"
	"fmt"
	"golang_note/day08_http/gin/rabbitmq"
	myRedis "golang_note/day08_http/gin/redis"
	"io/ioutil"
	"math"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/gin-gonic/gin"
)

//MultipartUploadInfo ...
type MultipartUploadInfo struct {
	FileHash   string  `json:"filehash" form:"filehash"`
	FileType   string  `json:"filetype" form:"filetype"`
	FileSize   float64 `json:"filesize" form:"filesize"`
	UploadID   string  `json:"uploadid" form:"uploadid"`
	ChunkSize  float64 `json:"chunksize" form:"chunksize"`
	ChunkCount int64   `json:"chunkcount" form:"chunkcount"`
}

//FileRouterinit ...,
func FileRouterinit(c *gin.Context) {
	var upInfo MultipartUploadInfo
	c.ShouldBind(&upInfo)
	upInfo.ChunkSize = 5 * 1024 * 1024 //5M
	upInfo.UploadID = "USERNAME" + fmt.Sprintf("%x", time.Now().UnixNano())
	upInfo.ChunkCount = int64(math.Ceil(upInfo.FileSize / upInfo.ChunkSize))
	// fmt.Printf("%#v", upInfo)
	//初始化信息写入到redis缓存
	rConn := myRedis.RedisPool().Get()
	defer rConn.Close()
	rConn.Do("HSET", "MP_"+upInfo.UploadID, "chunkcount", upInfo.ChunkCount)
	rConn.Do("HSET", "MP_"+upInfo.UploadID, "filehash", upInfo.FileHash)
	rConn.Do("HSET", "MP_"+upInfo.UploadID, "filesize", upInfo.FileSize)
	rConn.Do("HSET", "MP_"+upInfo.UploadID, "filetype", upInfo.FileType)
	c.JSON(http.StatusOK, gin.H{
		"status": 0,
		"msg":    "初始化成功",
		"data":   upInfo,
	})
}

//UploadPartHandel ...
func UploadPartHandel(c *gin.Context) {
	uploadID := c.PostForm("uploadid")
	chunkIndex := c.PostForm("index")

	//获得文件句柄，用于存储分块内容
	fpath := "../../data/" + uploadID + "/" + chunkIndex
	os.MkdirAll(path.Dir(fpath), 0744)
	file, err := c.FormFile("f1")
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"status": 1,
			"msg":    "上传文件失败",
		})
		return
	}
	err = c.SaveUploadedFile(file, fpath)
	if err != nil {
		fmt.Println(err)
		return
	}
	rConn := myRedis.RedisPool().Get()
	defer rConn.Close()

	if err != nil {
		fmt.Println(err)
		return
	}

	rConn.Do("HSET", "MP_"+uploadID, "chkidx_"+chunkIndex, "1")
	c.JSON(http.StatusOK, gin.H{
		"status": 0,
		"msg":    "文件成功",
		"index":  chunkIndex,
	})
}

//CompleteUploadHandler 通知上传合并
func CompleteUploadHandler(c *gin.Context) {
	//1、解析参数
	upid := c.PostForm("uploadid")
	// username := c.PostForm("username")
	// filehash := c.PostForm("filehash")
	// filesize := c.PostForm("filesize")
	// filename := c.PostForm("filename")
	rConn := myRedis.RedisPool().Get()
	defer rConn.Close()
	// 3. 通过uploadid查询redis并判断是否所有分块上传完成
	data, err := redis.Values(rConn.Do("HGETALL", "MP_"+upid))
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"status": 1,
			"msg":    "complete upload failed",
		})
		return
	}
	totalCount := 0
	chunkCount := 0
	var filename string
	for i := 0; i < len(data); i += 2 {
		k := string(data[i].([]byte))
		v := string(data[i+1].([]byte))
		if k == "chunkcount" {
			totalCount, _ = strconv.Atoi(v)
		} else if strings.HasPrefix(k, "chkidx_") && v == "1" {
			chunkCount++
		} else if k == "filetype" {
			filename = v
		}
	}
	if totalCount != chunkCount {
		c.JSON(http.StatusOK, gin.H{
			"status": 1,
			"msg":    "invalid request",
		})
		return
	}
	filepath := path.Join("../../data/", upid, "/1")
	file1, err := os.OpenFile(filepath, os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Println("openfile failed", err)
		return
	}
	for i := 2; i <= totalCount; i++ {
		filepath = path.Join("../../data/", upid, strconv.Itoa(i))

		context, err := ioutil.ReadFile(filepath)
		if err != nil {
			fmt.Println("append file failed", i, err)
			return
		}
		_, err = file1.Write(context)
		if err != nil {
			return
		}
		os.Remove(filepath)
	}
	file1.Close()
	newpath := path.Join("../../data/", upid, filename)
	err = os.Rename(filepath, newpath)
	if err != nil {
		fmt.Println("rename failed", err)
		return
	}
	msg := rabbitmq.TransferData{
		CurLocation:  newpath,
		DestLocation: filename,
	}
	pubData, _ := json.Marshal(msg)
	suc := rabbitmq.Publish(rabbitmq.TransExchangeName, rabbitmq.TransOSSRoutingKey, pubData)
	if !suc {
		//TODO
	}
	c.JSON(http.StatusOK, gin.H{
		"status": 0,
		"msg":    "合并成功",
	})
}
